d7e930b71ad2e14aacf33983572317a8ae36a3f4,src/org/exist/storage/recovery/RecoveryManager.java,RecoveryManager,doRecovery,#File#JournalReader#number#,169

Before Change


     * @throws LogException
     */
    private void doRecovery(File last, JournalReader reader, long lastLsn) throws LogException {
        if (LOG.isDebugEnabled())
            LOG.debug("Running recovery ...");
        logManager.setInRecovery(true);
        
        // map to track running transactions

After Change


     */
    private void doRecovery(int txnCount, File last, JournalReader reader, long lastLsn) throws LogException {
        if (LOG.isInfoEnabled())
            LOG.info("Running recovery ...");
        logManager.setInRecovery(true);

        try {
            // map to track running transactions
            Long2ObjectHashMap runningTxns = new Long2ObjectHashMap();

            // ------- REDO ---------
            if (LOG.isInfoEnabled())
                LOG.info("First pass: redoing " + txnCount + " transactions...");
            ProgressBar progress = new ProgressBar("Redo ", last.length());
            Loggable next = null;
            int redoCnt = 0;
            try {
                while ((next = reader.nextEntry()) != null) {
                    SanityCheck.ASSERT(next.getLogType() != LogEntryTypes.CHECKPOINT,
                            "Found a checkpoint during recovery run! This should not ever happen.");
                    if (next.getLogType() == LogEntryTypes.TXN_START) {
                        // new transaction starts: add it to the transactions table
                        runningTxns.put(next.getTransactionId(), next);
                    } else if (next.getLogType() == LogEntryTypes.TXN_COMMIT) {
                        // transaction committed: remove it from the transactions table
                        runningTxns.remove(next.getTransactionId());
                        redoCnt++;
                    } else if (next.getLogType() == LogEntryTypes.TXN_ABORT) {
                        // transaction aborted: remove it from the transactions table
                        runningTxns.remove(next.getTransactionId());
                    }
        //            LOG.debug("Redo: " + next.dump());
                    // redo the log entry
                    next.redo();
                    progress.set(Lsn.getOffset(next.getLsn()));
                    if (next.getLsn() == lastLsn)
                        break; // last readable entry reached. Stop here.
                }
            } catch (Exception e) {
                LOG.warn("Exception caught while redoing transactions. Aborting recovery.", e);
                if (next != null)
                    LOG.warn("Log entry that caused the exception: " + next.dump());
                throw new LogException("Recovery aborted");
            } finally {
                LOG.info("Redo processed " + redoCnt + " out of " + txnCount + " transactions.");
            }

            // ------- UNDO ---------
            if (LOG.isInfoEnabled())
                LOG.info("Second pass: undoing dirty transactions. Uncommitted transactions: " +
                        runningTxns.size());
            // see if there are uncommitted transactions pending
            if (runningTxns.size() > 0) {
                // do a reverse scan of the log, undoing all uncommitted transactions
                try {
                    while((next = reader.previousEntry()) != null) {
                        if (next.getLogType() == LogEntryTypes.TXN_START) {
                            if (runningTxns.get(next.getTransactionId()) != null) {
                                runningTxns.remove(next.getTransactionId());
                                if (runningTxns.size() == 0)
                                    // all dirty transactions undone
                                    break;
                            }
                        } else if (next.getLogType() == LogEntryTypes.TXN_COMMIT) {
                            // ignore already committed transaction
                        } else if (next.getLogType() == LogEntryTypes.CHECKPOINT) {
                            // found last checkpoint: undo is completed
                            break;
                        }

                        // undo the log entry if it belongs to an uncommitted transaction
                        if (runningTxns.get(next.getTransactionId()) != null) {
    //					LOG.debug("Undo: " + next.dump());
                            next.undo();
                        }
                    }
                } catch (Exception e) {
                    LOG.warn("Exception caught while undoing dirty transactions. Remaining transactions " +
                            "to be undone: " + runningTxns.size(), e);
                    if (next != null)
                        LOG.warn("Log entry that caused the exception: " + next.dump());
                    throw new LogException("Recovery aborted");
                }
            }